﻿#include <map>
#include <string>
#include <iostream>
#include "Move.h"
#include "DebugBench.h"
#include "Engine.h"
#include "Watch.h"
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::map;

#define STR_RESULT_OK "-- OK --";
#define STR_RESULT_KO "** KO **";

void DebugBench::LaunchBench()
{
	map<string,string> simplePositionsDB;// = new Hashtable();
	map<string,string> mediumPositionsDB;// = new Hashtable();
	map<string,string> complexPositionsDB;// = new Hashtable();

	initSimplePositionsDB(simplePositionsDB);
	initMediumPositionsDB(mediumPositionsDB);
	initComplexPositionsDB(complexPositionsDB);

	cout << "*****************************************\n"
		"** PERFORMANCE TEST SET                **\n"
		"*****************************************\n"
		" 1 - BenchMoveGeneration                 \n"
		" 2 - BenchSimplePositions                \n"
		" 3 - BenchMediumPositions                \n"
		" 4 - BenchComplexPositions               \n"
		"*****************************************\n";

	cout << "Bench:";
	int choice;
	cin >> choice;
	/*
	Console.Write("Duration (ms):");
	prompt = Console.ReadLine();
	int duration;
	Int32.TryParse(prompt, out duration);
	*/
	switch (choice)
	{
	case 1:
		BenchMoveGeneration(simplePositionsDB);
		break;
	case 2:
		BenchSimplePositionsDB(simplePositionsDB);
		break;
	case 3:
		BenchMediumPositionsDB(mediumPositionsDB);
		break;
	case 4:
		BenchComplexPositionsDB(complexPositionsDB);
		break;

	default:
		break;
	}
}

void DebugBench::BenchComplexPositionsDB(map<string,string> & complexPositionsDB)
{
	//throw new NotImplementedException();
}

void DebugBench::BenchMediumPositionsDB(map<string,string> & mediumPositionsDB)
{
	//throw new NotImplementedException();
}

void DebugBench::BenchSimplePositionsDB(map<string,string> & simplePositionsDB)
{
	//throw new NotImplementedException();
}

void DebugBench::BenchMoveGeneration(map<string,string> & positionsDB)
{

	const int MAXMGEN = 1000;
	Watch watch1,watch2;// = new Watch();
	vector<Move> legalMoves;// = new vector<Move>();
	vector<Move> pseudoLegal;// = new vector<Move>();
	Position pos;// = new Position();
	vector<long> fiftyRepRulesCheck;// = new vector<long>();
	vector<long> pseudoLegTime;// = new vector<long>();
	vector<long> legalizeTime;// = new vector<long>();
	vector<long> sortmoves;// = new vector<long>();


	vector<double> pseudoLegMoveGen, legalMoveGen;

	for(map<string,string>::iterator item = positionsDB.begin(); item != positionsDB.end() ; item++)
		//foreach (DictionaryEntry item in positionsDB)
	{
		string fen = (*item).first;
		pos.SetFromFEN(fen);

		// pseudo legal moves
		watch1.Start();
		for(int i=0; i<MAXMGEN;i++)
		{
			ChessEngine.GetPossibleMoves(pos,legalMoves);
		}
		watch1.Stop();

		// legal moves
		watch2.Start();
		for(int i=0; i<MAXMGEN;i++)
		{
			ChessEngine.GetLegalMoves(pos,legalMoves);
		}
		watch2.Stop();

		pseudoLegMoveGen.push_back((double)watch1.GetElapsedTime());
		legalMoveGen.push_back((double)watch2.GetElapsedTime());

		
		
		
		//watch.Reset();

		//// fiftyRepRulesCheck
		//cout << "fiftyRepRulesCheck" << endl;
		//watch.Start();
		//int doNothing;
		//for (int i = 0; i < MAXMGEN; i++)
		//{
		//	pseudoLegal.clear();
		//	if (ChessEngine.IsFiftyMovesDraw(pos) || ChessEngine.IsThreeTimesRepetitionDraw(pos))
		//		doNothing = i;
		//}
		//watch.Stop();
		//fiftyRepRulesCheck.push_back(watch.GetElapsedTime() / MAXMGEN);

		//watch.Reset();

		//// pseudoLegTime
		//cout << "pseudoLegTime" << endl;
		//watch.Start();
		//for (int i = 0; i < MAXMGEN; i++)
		//{
		//	pseudoLegal.clear();
		//	ChessEngine.GetPseudoLegalMoves(pos, pseudoLegal);
		//}
		//watch.Stop();
		//pseudoLegTime.push_back(watch.GetElapsedTime() / MAXMGEN);

		//watch.Reset();

		//// legalizeTime
		//cout << "legalizeTime" << endl;
		//watch.Start();
		//for (int i = 0; i < MAXMGEN; i++)
		//{
		//	//pseudoLegal.CopyTo(legalMoves);
		//	legalMoves.clear();//legalMoves;// = new vector<Move>(pseudoLegal);
		//	ChessEngine.GetLegalMoves(pos, legalMoves);
		//}
		//watch.Stop();
		//legalizeTime.push_back(watch.GetElapsedTime() / MAXMGEN);

		//watch.Reset();

		//// sort moves
		//cout << "sort moves" << endl;
		//watch.Start();
		//
		//for (int i = 0; i < MAXMGEN; i++)
		//{
		//	vector<Move> unsortedMoves(legalMoves);
		//	//unsortedMoves.clear();// = new vector<Move>(legalMoves);
		//	// TO BE IMPLEMENTED
		//	//unsortedMoves.Sort();
		//}
		//watch.Stop();
		//sortmoves.push_back(watch.GetElapsedTime() / MAXMGEN);

	}

	//long meanfiftyRepRulesCheck = Mean(fiftyRepRulesCheck);
	//long meanpseudoLegTime = Mean(pseudoLegTime);
	//long meanlegalizeTime = Mean(legalizeTime);
	//long meanSortmoves = Mean(sortmoves);

		double meanPseudoLegMoveGen = Mean(pseudoLegMoveGen);
		double meanLegalMoveGen = Mean(legalMoveGen);

	////long meantotalGenTime = Mean(totalGenTime);
	//long meantotalGenTime = meanfiftyRepRulesCheck + meanpseudoLegTime + meanlegalizeTime + meanSortmoves;

	//long avfiftyRepRulesCheck = meantotalGenTime?(100 * meanfiftyRepRulesCheck) / meantotalGenTime : 0;
	//long avtotalGenTime = meantotalGenTime?(100 * meantotalGenTime) / meantotalGenTime : 0;
	//long avpseudoLegTime = meantotalGenTime?(100 * meanpseudoLegTime) / meantotalGenTime : 0;
	//long avlegalizeTime = meantotalGenTime?(100 * meanlegalizeTime) / meantotalGenTime : 0;
	//long avSortmoves = meantotalGenTime?(100 * meanSortmoves) / meantotalGenTime : 0;

	cout << "********************************************"<< endl;
	cout << "* BenchMoveGeneration                      *"<< endl;
	cout << "********************************************"<< endl;
	cout << "Nb of benched positions: " << positionsDB.size() << "x" << MAXMGEN  << "=" << positionsDB.size() * MAXMGEN << endl;
	cout << " ------------- Pseudo legal moves generation" << endl;
	cout << "time per position: " << (int)meanPseudoLegMoveGen << " microsec   " << endl;
	cout << "Average speed:     " << (meanPseudoLegMoveGen?(int)(1000000/meanPseudoLegMoveGen):0) << " nps" << endl;
	cout << " -------------------- Legal moves generation" << endl;
	cout << "time per position: " << (int)meanLegalMoveGen << " microsec   " << endl;
	cout << "Average speed:     " << (meanLegalMoveGen?(int)(1000000/meanLegalMoveGen):0) << " nps" << endl;

	/*
	cout << "fiftyRepRules: " << meanfiftyRepRulesCheck << " ms    " << avfiftyRepRulesCheck << "%" << endl;
	cout << "pseudoLegal:   " << meanpseudoLegTime << " ms    " << avpseudoLegTime << "%"<< endl;
	cout << "legalize:      " << meanlegalizeTime << " ms    " << avlegalizeTime << "%"<< endl;
	cout << "sortmoves:     " << meanSortmoves << " ms    " << avSortmoves << "%"<< endl;
	cout << "totalGenTime:  " << meantotalGenTime << " ms    " << avtotalGenTime << "%" << endl;
	*/
	cout << "********************************************"<< endl;

}

double DebugBench::Mean(vector<double> results)
{
	double sum = 0;
	for(vector<double>::iterator nb = results.begin(); nb != results.end(); nb++)
		//foreach (long nb in results)
	{
		sum += (*nb);
	}
	return sum / results.size();
}

void DebugBench::initComplexPositionsDB(map<string,string> & complexPositionsDB)
{
	// TODO: change this set for a more representative sample (ex: Nunn problems or middle game positions)
	complexPositionsDB["k6K/8/8/8/8/8/PPPPPPPP/8 w - - 1 1"] = "pawn moves 1:a2a3 a2a4 b2b3 b2b4 c2c3 c2c4 d2d3 d2d4 e2e3 e2e4 f2f3 f2f4 g2g3 g2g4 h2h3 h2h4 h8g8 h8g7 h8h7"; //w pawn moves
	complexPositionsDB["k1K5/p7/8/8/8/8/8/8 b - - 1 1"] = "pawn + king:a7a6 a7a5"; //b basic pawn moves
	complexPositionsDB["rn2kbnr/2Nbpppp/8/2pP4/3P1B2/P7/P1P2PPP/R2QKB1R b KQkq - 1 1"] = "check - only one move 1:e8d8";
	complexPositionsDB["r3k2r/8/8/8/8/p6p/P6P/R3K3 w KQkq - 1 1"] = "casteling moves:a1b1 a1c1 a1d1 e1d1 e1d2 e1e2 e1f2 e1f1 e1c1";
	complexPositionsDB["rnbqkbnr/4pQ2/2pp3p/6B1/p1BPP3/2N2NP1/PPP2PP1/R3K2R b KQkq - 0 11"] = "check - only one move 2:e8d7";
	complexPositionsDB["K6k/8/8/8/8/8/8/8 b - - 0 1"] = "king moves:h8g8 h8g7 h8h7";
	complexPositionsDB["rB2kbbr/1p6/8/p6Q/2PPP3/8/PP3PPP/R4RK1 b - - 0 19"] = "blacks in check - 5 moves:h8h5 g8f7 e8e7 e8d7 e8d8";
	complexPositionsDB["4kb1r/4b3/8/7B/8/8/8/6K1 b k - 0 19"] = "Blacks in check - escape or take(3 moves):h8h5 e8d8 e8d7";
	complexPositionsDB["K1k5/8/8/8/3R4/8/8/8 w - - 0 1 "] = "tower moves 1:d4d1 d4d2 d4d3 d4d5 d4d6 d4d7 d4d8 d4a4 d4b4 d4c4 d4e4 d4f4 d4g4 d4h4 a8a7";
	complexPositionsDB["K1k5/8/3p4/8/1P1R2p1/3P4/8/8 w - - 0 1 "] = "tower moves 2:d4d5 d4d6 d4c4 d4e4 d4f4 d4g4 a8a7 b4b5";
	complexPositionsDB["K1k5/8/8/8/8/3B4/8/8 w - - 0 1 "] = "bishop moves 1:d3b1 d3c2 d3e4 d3f5 d3g6 d3h7 d3a6 d3b5 d3c4 d3e2 d3f1 a8a7";
	complexPositionsDB["K1k5/4B3/1B6/8/6B1/3B4/8/8 w - - 0 1 "] = "bishop moves 2:d3b1 d3c2 d3e4 d3f5 d3g6 d3h7 d3a6 d3b5 d3c4 d3e2 d3f1 g4h3 g4h5 g4d1 g4e2 g4f3 g4f5 g4e6 g4d7 g4c8 e7f8 e7d8 e7f6 e7g5 e7h4 e7d6 e7c5 e7b4 e7a3 b6a7 b6c7 b6d8 b6a5 b6c5 b6d4 b6e3 b6f2 b6g1 a8a7";
	complexPositionsDB["8/8/8/4N3/8/8/8/k6K w - - 10 10"] = "knight moves 1:e5f7 e5g6 e5g4 e5f3 e5d3 e5c4 e5c6 e5d7 h1h2 h1g2 h1g1"; // knight moves
	complexPositionsDB["k7/8/8/pP6/8/8/8/K7 w - a6 10 10"] = "en-passant + pawn push 1 (5 moves):a1a2 a1b2 a1b1 b5b6 b5a6"; // kings + en passant
	complexPositionsDB["k7/8/8/1Pp5/8/8/8/K7 w - c6 10 10"] = "en-passant + pawn push 2 (5 moves):a1a2 a1b2 a1b1 b5b6 b5c6"; // kings + en passant

}

void DebugBench::initMediumPositionsDB(map<string,string> & mediumPositionsDB)
{
	// TODO: change this set for a more representative sample
	mediumPositionsDB["k6K/8/8/8/8/8/PPPPPPPP/8 w - - 1 1"] = "pawn moves 1:a2a3 a2a4 b2b3 b2b4 c2c3 c2c4 d2d3 d2d4 e2e3 e2e4 f2f3 f2f4 g2g3 g2g4 h2h3 h2h4 h8g8 h8g7 h8h7"; //w pawn moves
	mediumPositionsDB["k1K5/p7/8/8/8/8/8/8 b - - 1 1"] = "pawn + king:a7a6 a7a5"; //b basic pawn moves
	mediumPositionsDB["rn2kbnr/2Nbpppp/8/2pP4/3P1B2/P7/P1P2PPP/R2QKB1R b KQkq - 1 1"] = "check - only one move 1:e8d8";
	mediumPositionsDB["r3k2r/8/8/8/8/p6p/P6P/R3K3 w KQkq - 1 1"] = "casteling moves:a1b1 a1c1 a1d1 e1d1 e1d2 e1e2 e1f2 e1f1 e1c1";
	mediumPositionsDB["rnbqkbnr/4pQ2/2pp3p/6B1/p1BPP3/2N2NP1/PPP2PP1/R3K2R b KQkq - 0 11"] = "check - only one move 2:e8d7";
	mediumPositionsDB["K6k/8/8/8/8/8/8/8 b - - 0 1"] = "king moves:h8g8 h8g7 h8h7";
	mediumPositionsDB["rB2kbbr/1p6/8/p6Q/2PPP3/8/PP3PPP/R4RK1 b - - 0 19"] = "blacks in check - 5 moves:h8h5 g8f7 e8e7 e8d7 e8d8";
	mediumPositionsDB["4kb1r/4b3/8/7B/8/8/8/6K1 b k - 0 19"] = "Blacks in check - escape or take(3 moves):h8h5 e8d8 e8d7";
	mediumPositionsDB["K1k5/8/8/8/3R4/8/8/8 w - - 0 1 "] = "tower moves 1:d4d1 d4d2 d4d3 d4d5 d4d6 d4d7 d4d8 d4a4 d4b4 d4c4 d4e4 d4f4 d4g4 d4h4 a8a7";
	mediumPositionsDB["K1k5/8/3p4/8/1P1R2p1/3P4/8/8 w - - 0 1 "] = "tower moves 2:d4d5 d4d6 d4c4 d4e4 d4f4 d4g4 a8a7 b4b5";
	mediumPositionsDB["K1k5/8/8/8/8/3B4/8/8 w - - 0 1 "] = "bishop moves 1:d3b1 d3c2 d3e4 d3f5 d3g6 d3h7 d3a6 d3b5 d3c4 d3e2 d3f1 a8a7";
	mediumPositionsDB["K1k5/4B3/1B6/8/6B1/3B4/8/8 w - - 0 1 "] = "bishop moves 2:d3b1 d3c2 d3e4 d3f5 d3g6 d3h7 d3a6 d3b5 d3c4 d3e2 d3f1 g4h3 g4h5 g4d1 g4e2 g4f3 g4f5 g4e6 g4d7 g4c8 e7f8 e7d8 e7f6 e7g5 e7h4 e7d6 e7c5 e7b4 e7a3 b6a7 b6c7 b6d8 b6a5 b6c5 b6d4 b6e3 b6f2 b6g1 a8a7";
	mediumPositionsDB["8/8/8/4N3/8/8/8/k6K w - - 10 10"] = "knight moves 1:e5f7 e5g6 e5g4 e5f3 e5d3 e5c4 e5c6 e5d7 h1h2 h1g2 h1g1"; // knight moves
	mediumPositionsDB["k7/8/8/pP6/8/8/8/K7 w - a6 10 10"] = "en-passant + pawn push 1 (5 moves):a1a2 a1b2 a1b1 b5b6 b5a6"; // kings + en passant
	mediumPositionsDB["k7/8/8/1Pp5/8/8/8/K7 w - c6 10 10"] = "en-passant + pawn push 2 (5 moves):a1a2 a1b2 a1b1 b5b6 b5c6"; // kings + en passant

}

void DebugBench::initSimplePositionsDB(map<string,string> & simplePositionsDB)
{
	// TODO: change this set for a more representative sample (ex: endgames with few pieces)
	simplePositionsDB["k6K/8/8/8/8/8/PPPPPPPP/8 w - - 1 1"] = "pawn moves 1:a2a3 a2a4 b2b3 b2b4 c2c3 c2c4 d2d3 d2d4 e2e3 e2e4 f2f3 f2f4 g2g3 g2g4 h2h3 h2h4 h8g8 h8g7 h8h7"; //w pawn moves
	simplePositionsDB["k1K5/p7/8/8/8/8/8/8 b - - 1 1"] = "pawn + king:a7a6 a7a5"; //b basic pawn moves
	simplePositionsDB["rn2kbnr/2Nbpppp/8/2pP4/3P1B2/P7/P1P2PPP/R2QKB1R b KQkq - 1 1"] = "check - only one move 1:e8d8";
	simplePositionsDB["r3k2r/8/8/8/8/p6p/P6P/R3K3 w KQkq - 1 1"] = "casteling moves:a1b1 a1c1 a1d1 e1d1 e1d2 e1e2 e1f2 e1f1 e1c1";
	simplePositionsDB["rnbqkbnr/4pQ2/2pp3p/6B1/p1BPP3/2N2NP1/PPP2PP1/R3K2R b KQkq - 0 11"] = "check - only one move 2:e8d7";
	simplePositionsDB["K6k/8/8/8/8/8/8/8 b - - 0 1"] = "king moves:h8g8 h8g7 h8h7";
	simplePositionsDB["rB2kbbr/1p6/8/p6Q/2PPP3/8/PP3PPP/R4RK1 b - - 0 19"] = "blacks in check - 5 moves:h8h5 g8f7 e8e7 e8d7 e8d8";
	simplePositionsDB["4kb1r/4b3/8/7B/8/8/8/6K1 b k - 0 19"] = "Blacks in check - escape or take(3 moves):h8h5 e8d8 e8d7";
	simplePositionsDB["K1k5/8/8/8/3R4/8/8/8 w - - 0 1 "] = "tower moves 1:d4d1 d4d2 d4d3 d4d5 d4d6 d4d7 d4d8 d4a4 d4b4 d4c4 d4e4 d4f4 d4g4 d4h4 a8a7";
	simplePositionsDB["K1k5/8/3p4/8/1P1R2p1/3P4/8/8 w - - 0 1 "] = "tower moves 2:d4d5 d4d6 d4c4 d4e4 d4f4 d4g4 a8a7 b4b5";
	simplePositionsDB["K1k5/8/8/8/8/3B4/8/8 w - - 0 1 "] = "bishop moves 1:d3b1 d3c2 d3e4 d3f5 d3g6 d3h7 d3a6 d3b5 d3c4 d3e2 d3f1 a8a7";
	simplePositionsDB["8/8/8/4N3/8/8/8/k6K w - - 10 10"] = "knight moves 1:e5f7 e5g6 e5g4 e5f3 e5d3 e5c4 e5c6 e5d7 h1h2 h1g2 h1g1"; // knight moves
	simplePositionsDB["k7/8/8/pP6/8/8/8/K7 w - a6 10 10"] = "en-passant + pawn push 1 (5 moves):a1a2 a1b2 a1b1 b5b6 b5a6"; // kings + en passant
	simplePositionsDB["k7/8/8/1Pp5/8/8/8/K7 w - c6 10 10"] = "en-passant + pawn push 2 (5 moves):a1a2 a1b2 a1b1 b5b6 b5c6"; // kings + en passant
	
	// KO for getLegalMoves
	//simplePositionsDB["K1k5/4B3/1B6/8/6B1/3B4/8/8 w - - 0 1 "] = "bishop moves 2:d3b1 d3c2 d3e4 d3f5 d3g6 d3h7 d3a6 d3b5 d3c4 d3e2 d3f1 g4h3 g4h5 g4d1 g4e2 g4f3 g4f5 g4e6 g4d7 g4c8 e7f8 e7d8 e7f6 e7g5 e7h4 e7d6 e7c5 e7b4 e7a3 b6a7 b6c7 b6d8 b6a5 b6c5 b6d4 b6e3 b6f2 b6g1 a8a7";
	
}

void DebugBench::BenchSetFenPerfs()
{
	/*
	Watch wa = new Watch();
	Watch wb = new Watch();
	int nbPieces = 0;

	for (int i = 0; i < 1000; i++)
	{
	String fen = getRandomFEN();
	ArrayBoard aPos = new ArrayBoard();
	BitBoard bPos = new BitBoard();

	wa.Start();
	aPos.SetFromFEN(fen);
	wa.Stop();
	wb.Start();
	bPos.SetFromFEN(fen);
	wb.Stop();
	nbPieces += GetNbPieces(aPos);
	}
	System.cout << "Perfs for std call of SetFenPerfs (32 pieces/board)");
	System.cout << "ArrayBoard : " + 32 * wa.ElapsedTicks / nbPieces + " ticks/pos");
	System.cout << "BitBoard   : " + 32 * wb.ElapsedTicks / nbPieces + " ticks/pos");
	*/
}


void DebugBench::BenchGetLegalMovesPerfs()
{
	/*
	Watch wa;// = new Watch();
	Watch wb;// = new Watch();
	int nbPieces = 0;

	for (int i = 0; i < 1000; i++)
	{
	string fen = getRandomFEN();
	Position p = new Position();
	p.SetFromFEN(fen);

	wa.Start();
	ChessEngine.GetPossibleMoves(p);
	wa.Stop();
	wb.Start();
	ChessEngine.GetLegalMoves(p);
	wb.Stop();
	nbPieces += GetNbPieces(p);
	}
	cout << "Perfs for std call of GetLegalMoves (32 pieces/board)");
	cout << "GetPossibleMoves : " + 32 * wa.ElapsedTicks / nbPieces + " ticks/pos  (total:" + wa.ElapsedTicks + "ticks)");
	cout << "GetLegalMoves   : " + 32 * wb.ElapsedTicks / nbPieces + " ticks/pos (total:" + wb.ElapsedTicks + "ticks)");
	*/
}
